package com.xhj.mybatis.reflection.factory;

import com.xhj.mybatis.reflection.Reflector;

import java.lang.reflect.Constructor;
import java.util.*;
import java.util.stream.Collectors;

/**
 * DefaultObjectFactory
 *
 * @author XJks
 * @description 默认对象工厂。TODO:默认用 无参构造 或 匹配的构造器 反射实例化。
 */
public class DefaultObjectFactory implements ObjectFactory {

    /**
     * 创建对象的无参方法，调用带参数的方法，参数为 null
     *
     * @param type 要创建的对象的 Class 类型
     * @param <T>  对象的类型
     * @return T 创建的对象实例
     */
    @Override
    public <T> T create(Class<T> type) {
        return create(type, null, null);
    }

    /**
     * 创建对象的核心方法，可以指定构造方法参数类型和参数值
     *
     * @param type                要创建的对象的 Class 类型
     * @param constructorArgTypes 构造方法参数类型的列表（Class 类型列表）
     * @param constructorArgs     构造方法参数值的列表（实际参数值）
     * @param <T>                 对象的类型
     * @return T 创建的对象实例
     */
    @Override
    public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        // 先将接口类型转换为具体实现类
        Class<?> classToCreate = resolveInterface(type);
        // 调用实例化方法创建对象，并强制类型转换
        return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs);
    }

    /**
     * 实例化对象的方法，支持无参和有参构造函数
     *
     * @param type                要实例化的 Class 类型
     * @param constructorArgTypes 构造方法参数类型列表
     * @param constructorArgs     构造方法参数值列表
     * @param <T>                 对象的类型
     * @return 返回实例化后的对象
     */
    private <T> T instantiateClass(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
        try {
            // 声明一个构造方法对象
            Constructor<T> constructor;
            // 如果没有指定构造参数类型或参数值，使用无参构造函数
            if (constructorArgTypes == null || constructorArgs == null) {
                // 获取无参构造方法
                constructor = type.getDeclaredConstructor();
                try {
                    // 直接调用无参构造函数创建对象
                    return constructor.newInstance();
                } catch (IllegalAccessException e) {
                    // 如果构造方法不可访问（如 private），尝试设置为可访问
                    if (Reflector.canControlMemberAccessible()) {
                        // 设置构造方法为可访问
                        constructor.setAccessible(true);
                        // 再次尝试创建对象
                        return constructor.newInstance();
                    } else {
                        // 如果不能设置为可访问，则抛出异常
                        throw e;
                    }
                }
            }
            // 如果有参数，获取对应参数类型的构造函数
            // 获取有参构造方法
            constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[0]));
            try {
                // 调用有参构造函数创建对象
                return constructor.newInstance(constructorArgs.toArray(new Object[0]));
            } catch (IllegalAccessException e) {
                // 如果构造方法不可访问，尝试设置为可访问
                if (Reflector.canControlMemberAccessible()) {
                    // 设置构造方法为可访问
                    constructor.setAccessible(true);
                    // 再次尝试创建对象
                    return constructor.newInstance(constructorArgs.toArray(new Object[0]));
                } else {
                    // 如果不能设置为可访问，则抛出异常
                    throw e;
                }
            }
        } catch (Exception e) {
            // 构造失败时，拼接参数类型和值，抛出自定义异常，方便排查问题
            String argTypes = Optional.ofNullable(constructorArgTypes).orElseGet(Collections::emptyList)
                    .stream().map(Class::getSimpleName).collect(Collectors.joining(","));
            String argValues = Optional.ofNullable(constructorArgs).orElseGet(Collections::emptyList)
                    .stream().map(String::valueOf).collect(Collectors.joining(","));
            throw new RuntimeException("Error instantiating " + type + " with invalid types (" + argTypes + ") or values (" + argValues + "). Cause: " + e, e);
        }
    }

    /**
     * 将接口类型转换为具体实现类。
     * 例如：如果传入的是 List.class，会返回 ArrayList.class
     *
     * @param type 要转换的接口类型
     * @return Class<?> 转换后的具体实现类
     */
    protected Class<?> resolveInterface(Class<?> type) {
        // 声明一个变量，用于存储最终要创建的类类型
        Class<?> classToCreate;
        if (type == List.class || type == Collection.class || type == Iterable.class) {
            // 如果传入的类型是 List、Collection 或 Iterable 接口，则使用 ArrayList 作为具体实现类
            classToCreate = ArrayList.class;
        } else if (type == Map.class) {
            // 如果传入的类型是 Map 接口，则使用 HashMap 作为具体实现类
            classToCreate = HashMap.class;
        } else if (type == SortedSet.class) {
            // 如果传入的类型是 SortedSet 接口（有序集合），则使用 TreeSet 作为具体实现类
            classToCreate = TreeSet.class;
        } else if (type == Set.class) {
            // 如果传入的类型是 Set 接口，则使用 HashSet 作为具体实现类
            classToCreate = HashSet.class;
            // 如果不是上述常见接口类型，直接返回原始类型本身
        } else {
            classToCreate = type;
        }
        // 返回最终确定的类类型
        return classToCreate;
    }

    /**
     * 判断类型是否为 Collection 的子类或实现类
     *
     * @param type 要判断的对象的 Class 类型
     * @param <T>  对象的类型
     * @return boolean 如果是 Collection 的子类或实现类返回 true，否则返回 false
     */
    @Override
    public <T> boolean isCollection(Class<T> type) {
        return Collection.class.isAssignableFrom(type);
    }

}
